#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>

#include "recursive_package.h"
#include "u_peer.h"

static IPackage::Ptr serialIn(boost::iostreams::stream<boost::iostreams::array_source>& is, size_t size)
{
	if(size < 4)
	{
		return IPackage::Ptr();
	}
	int id;
	serial::read(is, id);
	IPackage::Ptr package = PackageMgr::getInstace()->get(id);
	if(package)
	{
		package->poll(is);	
	}

	return package;
}

void RecursivePackage::handle(IIoHandler::Ptr ioHandler)
{
	auto codec = boost::shared_dynamic_cast<DuplexCodec, IIoAttachment>(ioHandler->getAttachment());
	IPackage::Ptr package;
	if(codec)
	{
		package = serialIn(*codec);
	}
	else
	{
		boost::iostreams::stream<boost::iostreams::array_source> is(buf_.get(), buf_.size());
		package = ::serialIn(is, buf_.size());
	}
	if(package)
	{
		package->handle(ioHandler);
	}
	else
	{
		ioHandler->close();
	}
}

IPackage::Ptr RecursivePackage::serialIn(DuplexCodec& codec) const
{
	scoped_array<char> tmpBuf;
	codec.processIn(buf_, tmpBuf);
	boost::iostreams::stream<boost::iostreams::array_source> is(tmpBuf.get(), tmpBuf.size());
	return ::serialIn(is, tmpBuf.size());
}

void RecursivePackage::serialOut(IPackage& package, DuplexCodec& codec)
{
	size_t bufSize = package.size() + 4;
	scoped_array<char> tmpBuf(new char[bufSize], bufSize);
	boost::iostreams::stream<boost::iostreams::array_sink>  os(tmpBuf.get(), bufSize);
	serial::write(os, package.messageID());
	package.push(os);
	os.flush();

	codec.processOut(tmpBuf, buf_);
}

void RecursivePackage::serialOut(IPackage& package)
{
	size_t bufSize = package.size() + 4;
	if(buf_.size() < bufSize)
	{
		buf_.reset(new char[bufSize], bufSize);
	}
	boost::iostreams::stream<boost::iostreams::array_sink>  os(buf_.get(), bufSize);
	serial::write(os, package.messageID());
	package.push(os);
	os.flush();
}

void RecursivePackage::preSend(IIoHandler::Ptr ioHandler)
{
	auto codec = boost::shared_dynamic_cast<DuplexCodec, IIoAttachment>(ioHandler->getAttachment());
	if(codec)
	{
		scoped_array<char> tmpBuf;
		codec->processOut(buf_, tmpBuf);
		buf_.swap(tmpBuf);
	}
}

void RecursivePackage::send(IPackage& package, IIoHandler::Ptr ioHandler)
{
	auto wrapper = new RecursivePackage();
	wrapper->serialOut(package);
	wrapper->preSend(ioHandler);
	ioHandler->send(wrapper);
}